package com.way.filemanager.navigation;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;

import com.way.filemanager.R;
import com.way.filemanager.navigation.MyFileManager.FileChangeListener;
import com.way.filemanager.util.CommonFunc;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaMetadataRetriever;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.MediaStore.Images;
import android.util.DisplayMetrics;
import android.util.Log;

public class MyCache
{
    private Context mContext;
    private final static int MAX_ITEMS = 30;
    private int mEmptyIndex;

    private Item[] mItems = new Item[MAX_ITEMS];

    private enum Type
    {
        bitmap, video, apk, na
    }

    private HashMap<String, Drawable> mDrawables = new HashMap<String, Drawable>();

    private class Item
    {
        String path;
        long time;
        Drawable map;
        Type type;
        boolean delay;
        public void clear()
        {
            path = null;
            map = null;
        }
    }

    private FileChangeListener mFileChangeListener;
    public void setListener(FileChangeListener l) { mFileChangeListener = l; }

    private Drawable mApkDrawable;
    private Drawable mImageDrawable;
    private Drawable mVideoDrawable;
    private Drawable mNormalDrawable;
    private Drawable mUnknownDrawable;

    private int imageWidth;
    private int imageHeight;

    public MyCache(Context c)
    {

        mHandlerThread.start();

        mContext = c;
        mNormalDrawable = c.getResources().getDrawable(R.drawable.ic_file_normal);
        mUnknownDrawable = c.getResources().getDrawable(R.drawable.ic_unknown_normal);
        mApkDrawable = c.getResources().getDrawable(R.drawable.ic_apk_normal);
        mImageDrawable = c.getResources().getDrawable(R.drawable.ic_photo_normal);
        mVideoDrawable = c.getResources().getDrawable(R.drawable.ic_video_normal);

        imageWidth = (int) c.getResources().getDimension(R.dimen.hw_drawable_width);
        imageHeight = (int) c.getResources().getDimension(R.dimen.hw_drawable_height);

        prepareMethod();
        prepareTable();

        mEmptyIndex = 0;
        mHandler = new Handler(mHandlerThread.getLooper());

    }

    private String getSubByName(String n)
    {
        int dotIndex = n.lastIndexOf(".");
        return (dotIndex > 0) ? n.substring(dotIndex + 1).toLowerCase(Locale.getDefault()) : null;
    }

    private Drawable isNeedCacheBySub(String sub)
    {
        if(sub != null)
        {
            Drawable d = mDrawables.get(sub);
            if(d != null)
                return d;
            return mNormalDrawable;
        }
        return mUnknownDrawable;
    }


    public Drawable getFileIcon(String name, File file)
    {
        if(file.isFile() == false)
        {
            return mUnknownDrawable;
        }

        Drawable d = isNeedCacheBySub(getSubByName(name));
        if(d == mImageDrawable)
        {
            return getInCache(file, d, Type.bitmap);
        }
        else if(d == mVideoDrawable)
        {
            return getInCache(file, d, Type.video);
        }
        else if(d == mApkDrawable)
        {
            return getInCache(file, d, Type.apk);
        }

        return d;
    }

    private Drawable getInCache(File file, Drawable d, Type t)
    {
        String path = file.getAbsolutePath();
        long time = file.lastModified();
        Drawable cache = getInCache(path, time, t);
        return (cache != null) ? cache : d;
    }

    private Drawable getInCache(String path, long time, Type t)
    {
        for(Item item : mItems)
        {
            if(item != null && item.path != null && item.path.equals(path))
            {
                if(item.time == time)
                {
                    return item.map;
                }
                else
                {
                    item.time = time;
                    item.map = null;
                    MyFileManager.log("get incache time changed "+path);
                    doReflash();
                    return null;
                }
            }
        }
        additem(path, time, t);
        return null;
    }

    private void doDelayReflash()
    {
        mHandler.removeCallbacks(mDelayLoadRunnable);
        mHandler.postDelayed(mDelayLoadRunnable, 2000);
    }

    private void doReflash()
    {
        mHandler.removeCallbacks(mLoadRunnable);
        mHandler.post(mLoadRunnable);
    }

    private void doNotify()
    {
        mNotifyHandler.removeCallbacks(notifyRunnable);
        mNotifyHandler.post(notifyRunnable);
    }

    private void additem(String path, long time, Type t)
    {
        int index = mEmptyIndex;
        mEmptyIndex++;
        if(mEmptyIndex >= MAX_ITEMS)
        {
            mEmptyIndex = 0;
        }
        Item item = new Item();
        item.path = path;
        item.time = time;
        item.map = null;
        item.type = t;

        mItems[index] = item;

        MyFileManager.log("add item "+path);
        doReflash();
    }

    private HandlerThread mHandlerThread = new HandlerThread("mycache");
    private Handler mHandler;
    private Handler mNotifyHandler = new Handler();
    private Runnable mDelayLoadRunnable = new LoadRunnable(true);
    private Runnable mLoadRunnable = new LoadRunnable(false);

    private class LoadRunnable implements Runnable
    {
        private int count;
        private boolean isDelay;
        public LoadRunnable(boolean d)
        {
            isDelay = d;
        }

        private Item getNextItem()
        {
            Item item;
            while(count < MAX_ITEMS)
            {
                item = mItems[count++];
                if(item != null && item.path != null
                        && ((isDelay == false && item.map == null) || (isDelay == true && item.delay == true))
                        && item.type != Type.na)
                {
                    return item;
                }
            }
            return null;
        }

        @Override
        public void run()
        {
            Item item = null;
            MyFileManager.log("got to delay "+isDelay);
            count = 0;
            while((item = getNextItem()) != null)
            {
                MyFileManager.log("got to parse "+item.path +", t "+item.type);

                if(item.type == Type.bitmap)
                {
                    try {
                        BitmapFactory.Options op = new BitmapFactory.Options();
                        op.inJustDecodeBounds = true;
                        BitmapFactory.decodeFile(item.path, op);
                        if(op.outHeight > 0 && op.outWidth > 0)
                        {
                            op.inJustDecodeBounds = false;
                            op.inSampleSize =  (op.outHeight * imageWidth + op.outWidth * imageHeight)
                                    /(2 * imageHeight *imageWidth);
                            Bitmap b = BitmapFactory.decodeFile(item.path, op);
                            if(op.outHeight > 0 && op.outWidth > 0)
                            {
                                Bitmap temp1 = CommonFunc.ResizeBitmap2fitDrawable(mContext,b);
                                BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), temp1);
                                drawable.setBounds(0, 0, imageWidth, imageHeight);
                                item.map = drawable;
                                item.delay = false;
                            }
                            else
                            {
                                item.map = mImageDrawable;
                                item.delay = true;
                                doDelayReflash();
                                MyFileManager.log("scale map size error : "+item.path);
                            }
                        }
                        else
                        {
                            item.map = mImageDrawable;
                            item.delay = false;
                            MyFileManager.log("got map size error : "+item.path);
                        }
                    } catch (Exception e) {
                        MyFileManager.log("parse map error: "+item.path, e);
                        item.map = mImageDrawable;
                    }
                    doNotify();
                }
                else if(item.type == Type.video)
                {
                    Bitmap b = null;
                    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
                    try {
                        retriever.setDataSource(item.path);
                        b = retriever.getFrameAtTime(-1);
                        int width = b.getWidth();
                        int height = b.getHeight();
                        if(width > 0 && height > 0)
                        {
                            float s = ((float)(height) * (float)imageWidth + (float)(height) * imageHeight)/(2f * (float)imageHeight *(float)imageWidth);
                            b = Bitmap.createScaledBitmap(b, Math.round(width/s), Math.round(height/s), true);
                            BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), b);
                            drawable.setBounds(0, 0, imageWidth, imageHeight);
                            item.map = drawable;
                        }
                        else
                        {
                            item.map = mVideoDrawable;
                        }

                    } catch (Exception e) {
                        MyFileManager.log("parse video fail: "+item.path, e);
                        item.map = mVideoDrawable;
                    } finally {
                        try {
                            retriever.release();
                        } catch (Exception e) {
                            MyFileManager.log("retriever.release fail: "+item.path, e);
                        }
                    }
                    doNotify();
                }
                else if(item.type == Type.apk)
                {
                    Drawable icon = CommonFunc.showUninstallAPKIcon(item.path, mContext);
                    if (icon != null) {
                        item.map = icon;
                    }
                    else
                    {
                        item.map = mApkDrawable;
                    }
                    doNotify();
                }
                else
                {
                    item.clear();
                }
            }
        }
    };

    private Method mParsePackage;
    private Method mAddAssetPath;
    private Object mAssetMag;
    private Constructor<?> mParserCon;
    private DisplayMetrics mDm;
    private Resources mResources;
    private Constructor<Resources> mResCon;

    private void prepareMethod()
    {
        Class<?> pkgParserCls;
        try {
            pkgParserCls = Class.forName("android.content.pm.PackageParser");

            Class<?>[] typeArgs = new Class[1];
            typeArgs[0] = String.class;
            mParserCon = pkgParserCls.getConstructor(typeArgs);

            typeArgs = new Class[4];
            typeArgs[0] = File.class;
            typeArgs[1] = String.class;
            typeArgs[2] = DisplayMetrics.class;
            typeArgs[3] = Integer.TYPE;
            mParsePackage = pkgParserCls.getDeclaredMethod("parsePackage", typeArgs);

            mDm = new DisplayMetrics();
            mDm.setToDefaults();

            Class<?> assetMagCls = Class.forName("android.content.res.AssetManager");
            Constructor<?> assetMagCt = assetMagCls.getConstructor((Class[]) null);
            mAssetMag = assetMagCt.newInstance((Object[]) null);
            typeArgs = new Class[1];
            typeArgs[0] = String.class;
            mAddAssetPath = assetMagCls.getDeclaredMethod("addAssetPath", typeArgs);

            Object assetMag = assetMagCt.newInstance((Object[]) null);
            Resources res = mContext.getResources();
            typeArgs = new Class[3];
            typeArgs[0] = assetMag.getClass();
            typeArgs[1] = res.getDisplayMetrics().getClass();
            typeArgs[2] = res.getConfiguration().getClass();
            mResCon = Resources.class.getConstructor(typeArgs);
            Object[] valueArgs = new Object[3];
            valueArgs[0] = assetMag;
            valueArgs[1] = res.getDisplayMetrics();
            valueArgs[2] = res.getConfiguration();
            mResources = (Resources) mResCon.newInstance(valueArgs);

        } catch (Exception e) {
            e.printStackTrace();
            MyFileManager.log("fail to get method", e);
        }


    }

    private Runnable notifyRunnable = new Runnable()
    {
        @Override
        public void run() {
            if(mFileChangeListener != null)
            {
                mFileChangeListener.onChanged();
            }
        }

    };


    private void prepareTable()
    {
        Drawable d = mImageDrawable;
        mDrawables.put("bmp", d);
        mDrawables.put("jpeg", d);
        mDrawables.put("jpg", d);
        mDrawables.put("png", d);
        mDrawables.put("gif", d);

        d = mContext.getResources().getDrawable(R.drawable.ic_music_normal);
        mDrawables.put("mp3", d);
        mDrawables.put("wma", d);
        mDrawables.put("aac", d);
        mDrawables.put("ogg", d);
        mDrawables.put("wav", d);
        mDrawables.put("mid", d);
        mDrawables.put("midi", d);
        mDrawables.put("mpga", d);
        mDrawables.put("mp2", d);
        mDrawables.put("m3u", d);
        mDrawables.put("pls", d);
        mDrawables.put("m4a", d);
        mDrawables.put("m4b", d);
        mDrawables.put("m4p", d);
        mDrawables.put("xmf", d);
        mDrawables.put("rtttl", d);
        mDrawables.put("smf", d);
        mDrawables.put("imy", d);
        mDrawables.put("rtx", d);
        mDrawables.put("ota", d);

        d = mVideoDrawable;
        mDrawables.put("3gp", d);
        mDrawables.put("3gpp", d);
        mDrawables.put("rmvb", d);
        mDrawables.put("wmv", d);
        mDrawables.put("3g2", d);
        mDrawables.put("avi", d);
        mDrawables.put("m4u", d);
        mDrawables.put("m4v", d);
        mDrawables.put("mov", d);
        mDrawables.put("mkv", d);
        mDrawables.put("mp4", d);
        mDrawables.put("mpeg", d);
        mDrawables.put("mpg", d);
        mDrawables.put("mpg4", d);
        mDrawables.put("3gpp2", d);
        mDrawables.put("asf", d);

        d = mApkDrawable;
        mDrawables.put("apk", d);
        d = mContext.getResources().getDrawable(R.drawable.ic_txt_normal);
        mDrawables.put("txt", d);
        d = mContext.getResources().getDrawable(R.drawable.ic_zip_normal);
        mDrawables.put("zip", d);
        d = mContext.getResources().getDrawable(R.drawable.ic_pdf_normal);
        mDrawables.put("pdf", d);
        d = mContext.getResources().getDrawable(R.drawable.ic_ppt_normal);
        mDrawables.put("pps", d);
        mDrawables.put("ppt", d);
        d = mContext.getResources().getDrawable(R.drawable.ic_doc_normal);
        mDrawables.put("doc", d);
        mDrawables.put("docx", d);
        d = mContext.getResources().getDrawable(R.drawable.ic_xls_normal);
        mDrawables.put("xls", d);
        mDrawables.put("xlsx", d);

    }


}
